/*
把其他数据类型转换为Number
  Number([val])
    + 一般用于浏览器的隐式转换中
        @1 数学运算
        @2 isNaN检测
        @3 ==比较
        ...
    + 规则：
        @1 字符串转换为数字：空字符串变为0，如果出现任何非有效数字字符，结果都是NaN
        @2 把布尔转换为数字：true->1  false->0
        @3 null->0  undefined->NaN
        @4 Symbol无法转换为数字，会报错：Uncaught TypeError: Cannot convert a Symbol value to a number
        @5 BigInt去除“n”（超过安全数字的，会按照科学计数法处理）
        @6 把对象转换为数字：
          + 先调用对象的 Symbol.toPrimitive 这个方法，如果不存在这个方法
          + 再调用对象的 valueOf 获取原始值，如果获取的值不是原始值
          + 再调用对象的 toString 把其变为字符串
          + 最后再把字符串基于Number方法转换为数字
  parseInt([val])  parseFloat([val])
    + 一般用于手动转换
    + 规则：[val]值必须是一个字符串，如果不是则先转换为字符串；然后从字符串左侧第一个字符开始找，把找到的有效数字字符最后转换为数字「一个都没找到就是NaN」；遇到一个非有效数字字符，不论后面是否还有有效数字字符，都不再查找了；parseFloat可以多识别一个小数点；
  parseInt([val],[radix])
    从[val]左侧开始进行查找，找到所有符合[radix]进制的内容，然后把其按照[radix]进制转换为10进制！！
    + [radix]是设置的进制，取值有效范围是2~36之间，如果不在有效范围内，结果就是NaN
    + [radix]不写或者设置的为0，默认就是10「特殊情况：如果[val]是以“0x”开始的字符串，则默认值是16」


把其他数据类型转换为String
  转化规则：
    @1 拿字符串包起来
    @2 对象转字符串
      + String(对象)：按照 先找Symbol.toPrimitive -> 再看valueOf -> 最后toString 来处理
      + 对象.toString()：直接转换为字符串
      + 特殊：Object.prototype.toString，是用来检测数据类型的
  出现情况：
    @1 String([val]) 或者 [val].toString()
    @2 “+”除数学运算，还可能代表的字符串拼接
        + 有两边，一边是字符串，肯定是字符串拼接
        + 有两边，一边是对象，则可能是字符串拼接，还有可能是数学运算
        + 只出现在左边
        + ...
    ...


把其他数据类型转换为Boolean
  转换规则：除了“0/NaN/空字符串/null/undefined”五个值是false，其余都是true
  出现情况：
    @1 Boolean([val]) 或者 !/!!
    @2 条件判断
    ...


“==”相等，两边数据类型不同，需要先转为相同类型，然后再进行比较
    @1 对象==字符串  对象转字符串「Symbol.toPrimitive -> valueOf -> toString」
    @2 null==undefined  -> true   null/undefined和其他任何值都不相等
       null===undefined -> false
    @3 对象==对象  比较的是堆内存地址，地址相同则相等
    @4 NaN!==NaN  NaN和任何值(包含本身)都不相等 
    @5 除了以上情况，只要两边类型不一致，剩下的都是转换为数字，然后再进行比较的
“===”绝对相等，如果两边类型不同，则直接是false，不会转换数据类型「推荐」


Object.is([val1],[val2]) 检测两个值是否相等「ES6新增的」
  + 核心用的是“===”
  + 特殊：Object.is(NaN,NaN) => true
*/

/* let time = new Date();
console.log(Number(time));
// time[Symbol.toPrimitive]('number')  可以传递的值“string”、“default”、“number”
//   -> 1657875127208 时间戳「距离1970-01-01 00:00:00」 */

/* let num = new Number(10);
// 我们把这个操作（或者Object(10)）称之为“装箱”操作：把原始值变为对象类型的实例
console.log(Number(num));
// 这个过程，我们称之为“拆箱”操作：把实例对象转换为原始值
// num[Symbol.toPrimitive] -> undefined
// num.valueOf() -> 10 */

/* let arr = [10];
console.log(Number(arr));
// arr[Symbol.toPrimitive] -> undefined 不具备这个属性
// arr.valueOf() -> [10] 不是原始值
// arr.toString() -> '10'
// Number('10') -> 10  */


/* 
// 重写 Symbol.toPrimitive/valueOf/toString 三个中的任意方法，目的是让三次转换，分别返回1/2/3即可
var a = {
  i: 0
};
a[Symbol.toPrimitive] = function () {
  // this->a
  return ++this.i;
};
if (a == 1 && a == 2 && a == 3) {
  console.log('OK');
} 
*/

/* 
var a = [1, 2, 3];
a.toString = a.shift;
// 第一次执行shift  返回1  [2,3]
// 第二次执行shift  返回2  [3]
// ...
if (a == 1 && a == 2 && a == 3) {
  console.log('OK');
}   
*/

/* 
// 代码是在全局上下文中，我想获取a的值：首先看VO(G)是否存在，如果不存在再去GO中找...
// 全局上下文中，基于var声明的变量，是给GO(window)新增一个属性
// a === 1 其实就是获取window.a属性的值，然后和1进行比较 => 我们期望每次获取可以返回1/2/3，此时我们可以基于“数据劫持”
var i = 0;
Object.defineProperty(window, 'a', {
  get() {
    // 只要获取window.a的属性值，就会触发get函数
    return ++i;
  }
});
if (a === 1 && a === 2 && a === 3) {
  console.log('OK');
}  
*/

//===============================================
/* console.log(Number('')); //0
console.log(parseInt('')); //NaN
console.log(Number('12px')); //NaN
console.log(parseInt('12px')); //12
console.log(Number(null)); //0
console.log(parseInt(null)); //parseInt('null') NaN
console.log(Number([10, 20])); //NaN
console.log(parseInt([10, 20])); //parseInt('10,20') 10 */

/* parseInt('12px') -> parseInt('12px', 10) 
  从左到右查找符合10进制的内容 ‘12’
  把找到的内容看做10进制，转换为10进制 12 */

/* 
parseInt(12354, 5) -> parseInt('12354', 5)
  从左到右查找符合5进制的内容 '123'
  把其当做5进制，转换为10进制 「规则：把N进制转10进制-按权展开求和」
    每一项展开求和，每一项乘以 N^权重
    权重：个位0 十位1 百位2 ...
    1*5^2 + 2*5^1 + 3*5^0
      -> 25 + 10 + 3
      -> 38
*/


/*
  迭代arr中的每一项（本案例迭代5次）,每一次迭代都会把回调函数触发执行
    + 把当前迭代这项及其索引传递给回调函数
    + 接收回调函数的返回值，替换数组这一项「原始数组不变，以新数组返回」
  arr = arr.map((item,index)=>{
    //item:当前迭代这一项
    //index:迭代这一项的索引
    return '@';
  });
  arr -> ['@'*5...]
 */
let arr = [27.2, 0, '0013', '14px', 123];
arr = arr.map(parseInt);
console.log(arr);
/* 
parseInt(27.2,0) 处理的返回值替换第一项
  parseInt('27.2',10)
  找符合10进制的：'27'
  转换为10进制内容 27
parseInt(0,1)
  结果 NaN「因为设定的进制不在2~36之间」
parseInt('0013',2)
  找符合2进制的：'001'
  转换为10进制  0*2^2+0*2^1+1*2^0 = 0+0+1 = 1
parseInt('14px',3)
  找符合3进制的：'1'
  转换为10进制  1*3^0 = 1
parseInt(123,4) 
  parseInt('123',4)
  找符合4进制的：'123'
  转换为10进制  1*4^2+2*4^1+3*4^0 = 16+8+3 = 27
*/

//===============================================
/* console.log(1 + '1'); //'11'
console.log(1 + [1]); //尝试把[1]变为数字「没有Symbol.toPrimitive->valueOf获取的也不是原始值->开始调用toString() -> '1'」；转换中，出现了字符串，则字符串拼接 '11'
console.log(1 + new Number(1)); //尝试把new Number(1)变为数字「没有Symbol.toPrimitive->valueOf->1」；结果2； */


/* let i = '10';
i++;
console.log(i); //11

i = '10';
i += 1;
console.log(i); //'101'

i = '10';
i = i + 1;
console.log(i); //'101' */

/* let i = '10';
// i = Number(i);
// i = parseInt(i);
i = +i; //我以后经常写  +值 -> 把值转换为数字 「隐式转换  Number」
console.log(i); */

/* let result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;
// 100 + true + 21.2 + null -> 122.2
// 122.2 + undefined -> NaN
// NaN+"Tencent" -> "NaNTencent"
// ...
// "NaNTencentnull9false" */